<template>
  <div class="flex flex-col items-center font-sans mx-auto rounded-lg shadow-sm border-dashed border-1 component-container">
    <div class="flex items-center gap-4">
      <icon-button
        :style="{ 'border-color': buttonColor }"
        :class="buttonClass"
        v-if="recording"
        name="stop"
        @click="toggleRecording"
      />
      <icon-button
        :style="{ 'border-color': buttonColor }"
        :class="buttonClass"
        v-else
        name="mic"
        @click="toggleRecording"
      />
      <div v-if="!showDetail" style="width: 30px;">语音录制</div>
    </div>
    <div class="flex flex-col items-center justify-start" v-if="showDetail">
      <div>{{ recordedTime }}</div>
      <div class="text-sm font-bold">{{ successMessage }}</div>
      <div class="text-sm">{{ instructionMessage }}</div>
      <div class="text-sm text-red-400">{{ errorMessage }}</div>
      <figure class="mt-8 flex flex-col items-center justify-center">
        <audio controls :src="recordedAudio" type="audio/mpeg" class="mx-auto">
          您的浏览器不支持
          <code>语音</code> 组件.
        </audio>
        <figcaption class="text-sm mt-2">再提交前请检查您的语音</figcaption>
      </figure>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onBeforeUnmount } from 'vue';
import Service from "../../api/Service";
import Recorder from "../../lib/Recorder";
import convertTimeMMSS from "../../lib/Utils";
import IconButton from "./IconButton.vue";

const INSTRUCTION_MESSAGE = "点击图标进行录音";
const INSTRUCTION_MESSAGE_STOP = "点击图标再次停止录音";
const ERROR_MESSAGE = "使用录音组件失败. 请先刷新并允许录音组件的使用.";
const SUCCESS_MESSAGE = "录制成功!";
const SUCCESS_MESSAGE_SUBMIT = "成功提交录音！";
const ERROR_SUBMITTING_MESSAGE = "提交录音失败！请稍后尝试.";

const props = defineProps({
  time: { type: Number, default: 1 },
  bitRate: { type: Number, default: 128 },
  sampleRate: { type: Number, default: 44100 },
  backendEndpoint: { type: String },
  buttonColor: { type: String, default: "green" },
  afterRecording: { type: Function },
  successfulUpload: { type: Function },
  failedUpload: { type: Function },
  customUpload: { type: Function, default: null },
  showDetail: { type: Boolean, default: false }
});

const recording = ref(false);
const recordedAudio = ref(null);
const recordedBlob = ref(null);
const recorder = ref(null);
const successMessage = ref(null);
const errorMessage = ref(null);
const instructionMessage = ref(INSTRUCTION_MESSAGE);
const service = ref(null);

const buttonClass = computed(() => "mx-auto h-14 w-14 fill-current text-black cursor-pointer rounded-50 border-2 m-4 p-2");
const recordedTime = computed(() => {
  if (props.time && recorder.value?.duration >= props.time * 60) {
    toggleRecording();
  }
  return convertTimeMMSS(recorder.value?.duration);
});

const toggleRecording = () => {
  if (!props.showDetail) return;
  recording.value = !recording.value;
  if (recording.value) {
    initRecorder();
  } else {
    stopRecording();
  }
};

const initRecorder = () => {
  recorder.value = new Recorder({
    micFailed: micFailed,
    bitRate: props.bitRate,
    sampleRate: props.sampleRate,
  });
  recorder.value.start();
  successMessage.value = null;
  errorMessage.value = null;
  instructionMessage.value = INSTRUCTION_MESSAGE_STOP;
  service.value = new Service(props.backendEndpoint);
};

const stopRecording = () => {
  recorder.value.stop();
  const recordList = recorder.value.recordList();
  recordedAudio.value = recordList[0].url;
  recordedBlob.value = recordList[0].blob;
  if (recordedAudio.value) {
    successMessage.value = SUCCESS_MESSAGE;
    instructionMessage.value = null;
  }
  if (props.afterRecording) {
    props.afterRecording();
  }
};

const sendData = async () => {
  if (!recordedBlob.value) return;
  let result = null;
  if (props.customUpload) {
    result = await props.customUpload(recordedBlob.value, 2);
  } else {
    result = await service.value.postBackend(recordedBlob.value);
  }
  if (result) {
    errorMessage.value = null;
    successMessage.value = SUCCESS_MESSAGE_SUBMIT;
    if (props.successfulUpload) {
      props.successfulUpload(result);
    }
  } else {
    successMessage.value = null;
    errorMessage.value = ERROR_SUBMITTING_MESSAGE;
    if (props.failedUpload) {
      props.failedUpload();
    }
  }
};

const micFailed = () => {
  recording.value = false;
  instructionMessage.value = INSTRUCTION_MESSAGE;
  errorMessage.value = ERROR_MESSAGE;
};

onBeforeUnmount(() => {
  if (recording.value) {
    stopRecording();
  }
});

defineExpose({ sendData, initRecorder });
</script>
<style scoped lang="scss">
.component-container{
  width: 100%;
  height: 95%;
  border: 1px dashed var(--el-border-color);
  background-color: var(--el-fill-color-blank);
  border-radius: 6px;
  padding: 40px 10px;
  cursor: pointer;
}
.component-container:hover{
  color: var(--el-menu-active-color);
  border: 1px dashed var(--el-menu-active-color);
}
</style>